#include "httpclient_def.h"
#include "httpclient_wrapper.h"

#define MBEDTLS  1

#if MBEDTLS
#define DBG_PRINT(strFmt, args...) printf(strFmt, ##args)

#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
#define MBEDTLS_PLATFORM_FPRINTF_MACRO(stream, strFmt, args...) fprintf(stream, strFmt, ##args)
//#define MBEDTLS_PLATFORM_SNPRINTF_MACRO(s, n, strFmt, args...) snprintf

//#include <stdio.h>
//#include <stdlib.h>
//#define mbedtls_time       time
//#define mbedtls_time_t     time_t
//#define mbedtls_printf     MBEDTLS_PLATFORM_PRINTF_MACRO
#define mbedtls_fprintf    MBEDTLS_PLATFORM_FPRINTF_MACRO
//#define mbedtls_snprintf   MBEDTLS_PLATFORM_SNPRINTF_MACRO
#endif
#ifdef  _HARMONY_OS_PORTING_
#define fflush(x) ((void)0)
#include "mbedtls/threading.h"
#endif

#include "mbedtls/net_sockets.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/certs.h"
#include "mbedtls/x509.h"
#include "mbedtls/error.h"
#include "mbedtls/debug.h"
#include "mbedtls/timing.h"
#endif
#if MBEDTLS
    // 32 bit alignment macro
//#define ALIGN(size) ((size & 0xfffffffc) + ((size & 3) ? 4 : 0))
#ifdef ENABLE_IOT_DEBUG
#define MBEDTLS_DEBUG_BUFFER_SIZE 2048
#endif

/**
 * @brief TLS Connection Parameters
 *
 * Defines a type containing TLS specific parameters to be passed down to the
 * TLS networking layer to create a TLS secured socket.
 */
typedef struct {
	char *pRootCALocation;                ///< Pointer to string containing the filename (including path) of the root CA file.
	char *pDeviceCertLocation;            ///< Pointer to string containing the filename (including path) of the device certificate.
	char *pDevicePrivateKeyLocation;    ///< Pointer to string containing the filename (including path) of the device private key file.
	char *pDestinationURL;                ///< Pointer to string containing the endpoint of the MQTT service.
	UINT16  DestinationPort;            ///< Integer defining the connection port of the MQTT service.
	UINT32 timeout_ms;                ///< Unsigned integer defining the TLS handshake timeout value in milliseconds.
	BOOL ServerVerificationFlag;        ///< Boolean.  True = perform server certificate hostname validation.  False = skip validation \b NOT recommended.
} TLSConnectParams;

/**
 * @brief TLS Connection Parameters
 *
 * Defines a type containing TLS specific parameters to be passed down to the
 * TLS networking layer to create a TLS secured socket.
 */
typedef struct _TLSDataParams {
	mbedtls_entropy_context entropy;
	mbedtls_ctr_drbg_context ctr_drbg;
	mbedtls_ssl_context ssl;
	mbedtls_ssl_config conf;
	UINT32  flags;
	mbedtls_x509_crt cacert;
	mbedtls_x509_crt clicert;
	mbedtls_pk_context pkey;
	mbedtls_net_context server_fd;
}TLSDataParams;

/*
void _tls_set_connect_params(TLSConnectParams *pTlsConnectParams, char *pRootCALocation, char *pDeviceCertLocation,
								 char *pDevicePrivateKeyLocation, char *pDestinationURL,
								 UINT16 destinationPort, UINT32 timeout_ms, BOOL ServerVerificationFlag) {
	pTlsConnectParams->DestinationPort = destinationPort;
	pTlsConnectParams->pDestinationURL = pDestinationURL;
	pTlsConnectParams->pDeviceCertLocation = pDeviceCertLocation;
	pTlsConnectParams->pDevicePrivateKeyLocation = pDevicePrivateKeyLocation;
	pTlsConnectParams->pRootCALocation = pRootCALocation;
	pTlsConnectParams->timeout_ms = timeout_ms;
	pTlsConnectParams->ServerVerificationFlag = ServerVerificationFlag;
}

/ *
 * This is a function to do further verification if needed on the cert received
 * /

static int _tls_verify_cert(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags) {
	char buf[1024];
	((void) data);

	DBG_PRINT("\nVerify requested for (Depth %d):\n", depth);
	mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt);
	DBG_PRINT("%s", buf);

	if((*flags) == 0) {
		DBG_PRINT("  This certificate has no flags\n");
	} else {
		DBG_PRINT(buf, sizeof(buf), "  ! ", *flags);
		DBG_PRINT("%s\n", buf);
	}

	return 0;
}
*/

//#if !defined(__HARMONY_OS__)
static void my_debug( void *ctx, int level,
                      const char *file, int line,
                      const char *str )
{
    const char *p, *basename;

    /* Extract basename from file */
    for( p = basename = file; *p != '\0'; p++ )
        if( *p == '/' || *p == '\\' )
            basename = p + 1;

	#if !defined(__HARMONY_OS__)
    mbedtls_fprintf( (FILE *) ctx, "%s:%04d: |%d| %s", basename, line, level, str );
    fflush(  (FILE *) ctx  );
	#else
	UNUSED(ctx);
	printf("%s:%04d: |%d| %s\n", basename, line, level, str );
	#endif
}
//#endif

#if 0//(defined(_HARMONY_OS_PORTING_)
/*
 * Read at most 'len' characters, blocking for at most 'timeout' ms
 */
static int my_mbedtls_net_recv_timeout( void *ctx, unsigned char *buf, size_t len,
                      uint32_t timeout )
{
    int ret;
    struct timeval tv;
    fd_set read_fds;
    int fd = ((mbedtls_net_context *) ctx)->fd;

    DBG_PRINT("[%s(%d)]fd%d timeout (%dms)",__FUNCTION__,__LINE__, fd, timeout);
    if( fd < 0 )
        return( MBEDTLS_ERR_NET_INVALID_CONTEXT );

    FD_ZERO( &read_fds );
    FD_SET( fd, &read_fds );

    if ( timeout == 0 ) {
        timeout = 10*1000 ; /* unit : ms */
    }

    tv.tv_sec  = timeout / 1000;
    tv.tv_usec = ( timeout % 1000 ) * 1000;

    ret = select( fd + 1, &read_fds, NULL, NULL, timeout == 0 ? NULL : &tv );

    /* Zero fds ready means we timed out */
    if( ret == 0 ) {
		DBG_PRINT("[%s(%d)]fd%d Zero fds ready means we timed out",__FUNCTION__,__LINE__, fd);
        return( MBEDTLS_ERR_SSL_TIMEOUT );
    }

    if( ret < 0 )
    {
#if ( defined(_WIN32) || defined(_WIN32_WCE) ) && !defined(EFIX64) && \
    !defined(EFI32)
        if( WSAGetLastError() == WSAEINTR )
            return( MBEDTLS_ERR_SSL_WANT_READ );
#else
		#if defined(_HARMONY_OS_PORTING_)
        int socerrno = soc_geterr(fd);
        if( socerrno == 4)//EINTR )
            return( MBEDTLS_ERR_SSL_WANT_READ );
		#else
        if( errno == EINTR)
            return( MBEDTLS_ERR_SSL_WANT_READ );
		#endif
#endif

        return( MBEDTLS_ERR_NET_RECV_FAILED );
    }

    /* This call will not block */
    return( mbedtls_net_recv( ctx, buf, len ) );
}
#endif
#endif
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : Stdc: HTTPWrapperIsAscii
// Last updated : 15/05/2005
// Notes	    : Same as stdc: isascii
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperIsAscii(int c)
{
    return (!(c & ~0177));
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : Stdc: HTTPWrapperToUpper
// Last updated : 15/05/2005
// Notes	    : Convert character to uppercase.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperToUpper(int c)
{
    // -32
    if(HTTPWrapperIsAscii(c) > 0)
    {
        if(c >= 97 && c <= 122)
        {
            return (c - 32);
        }
    }

    return c;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : Stdc: HTTPWrapperToLower
// Last updated : 13/06/2006
// Notes	       : Convert character to lowercase.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperToLower(int c)
{
    // +32
    if(HTTPWrapperIsAscii(c) > 0)
    {
        if(c >= 65 && c <= 90)
        {
            return (c + 32);
        }
    }

    return c;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : Stdc: isalpha
// Last updated : 15/05/2005
// Notes	    : returns nonzero if c is a particular representation of an alphabetic character
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperIsAlpha(int c)
{

    if(HTTPWrapperIsAscii(c) > 0)
    {
        if( (c >= 97 && c <= 122) || (c >= 65 && c <= 90))
        {
            return c;
        }
    }
    return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : Stdc: isalnum
// Last updated : 15/05/2005
// Notes	    : returns nonzero if c is a particular representation of an alphanumeric character
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperIsAlNum(int c)
{
    if(HTTPWrapperIsAscii(c) > 0)
    {

        if(HTTPWrapperIsAlpha(c) > 0)
        {
            return c;
        }

        if( c >= 48 && c <= 57)
        {
            return c;
        }

    }
    return 0;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : HTTPWrapper_itoa
// Last updated : 15/05/2005
// Notes	    : same as stdc itoa() // hmm.. allmost the same
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

char* HTTPWrapperItoa(char *s,int a)
{
    unsigned int b;

    if(a > 2147483647)
    {
        return 0; // overflow
    }

    if (a < 0) b = -a, *s++ = '-';
    else b = a;
    for(;a;a=a/10) s++;
    for(*s='\0';b;b=b/10) *--s=b%10+'0';
    return s;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : HTTPWrapper_ShutDown
// Last updated : 15/05/2005
// Notes	    : Handles parameter changes in the socket shutdown()
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperShutDown (int s,int how)
{
    return shutdown(s,how);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : HTTPWrapper_GetSocketError
// Last updated : 15/05/2005
// Notes	    : WSAGetLastError Wrapper (Win32 Specific)
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperGetSocketError (int s)
{
	UNUSED(s);

#ifdef _WIN32
    return WSAGetLastError();
#else
    return errno;
#endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : HTTPWrapper_GetHostByName
// Last updated : 15/05/2005
// Notes	    : gethostbyname for Win32
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

unsigned long HTTPWrapperGetHostByName(char *name,unsigned long *address)
{
    HTTP_HOSTNET     *HostEntry;
    int     iPos = 0, iLen = 0,iNumPos = 0,iDots =0;
    long    iIPElement;
    char    c = 0;
    char    Num[4];
    int     iHostType = 0; // 0 : numeric IP

    // Check if the name is an IP or host
    iLen = strlen(name);
    for(iPos = 0; iPos <= iLen;iPos++)
    {
        c = name[iPos];
        if((c >= 48 && c <= 57)  || (c == '.') )
        {
            // c is numeric or dot
            if(c != '.')
            {
                // c is numeric
                if(iNumPos > 3)
                {
                    iHostType++;
                    break;
                }
                Num[iNumPos] = c;
                Num[iNumPos + 1] = 0;
                iNumPos ++;
            }
            else
            {
                iNumPos = 0;
                iDots++;
                iIPElement = atol(Num);
                if(iIPElement > 256 || iDots > 3)
                {
                    return 0; // error invalid IP
                }
            }
        }
        else
        {
            break; // this is an alpha numeric address type
        }
    }

    if(c == 0 && iHostType == 0 && iDots == 3)
    {
        iIPElement = atol(Num);
        if(iIPElement > 256)
        {
            return 0; // error invalid IP
        }
    }
    else
    {
        iHostType++;
    }

    if(iHostType > 0)
    {
        HostEntry = (HTTP_HOSTNET *)gethostbyname(name);
        if(HostEntry)
        {
            *(address) = *((u_long*)HostEntry->h_addr_list[0]);

            //*(address) = (unsigned long)HostEntry->h_addr_list[0];
            return 1; // Error
        }
        else
        {
            return 0; // OK
        }
    }

    else // numeric address - no need for DNS resolve
    {
        *(address) = inet_addr(name);
        return 1;

    }
    return 0;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : HTTPWrapper_GetRandomeNumber
// Last updated : 15/05/2005
// Notes	    : GetRandom number for Win32
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

void HTTPWrapperInitRandomeNumber()
{
    srand((unsigned int)time(NULL));
}

int HTTPWrapperGetRandomeNumber()
{
    int num;
    num = (int)(((double) rand()/ ((double)RAND_MAX+1)) * 16);
    return num;
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Section      : HTTPWrapper_GetRTC
// Last updated : 15/05/2005
// Author Name	: Eitan Michaelson
// Notes	    : Get uptime under Win32 & AMT
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

long HTTPWrapperGetUpTime()
{
#ifdef _WIN32

    long lTime = 0;

    lTime = (GetTickCount() / CLOCKS_PER_SEC);
    return lTime;

#else
/*#ifndef _ICATCH_OS

	struct timespec tp;

	clock_gettime(CLOCK_MONOTONIC , &tp);
	return tp.tv_sec;
#else*/
	return time(NULL);
/*#endif*/
#endif
}

int HTTPWrapperSSLConnect(long* pTlsHandle,const struct sockaddr_in *name, int namelen,char *hostname)
{
	UNUSED(name);
	UNUSED(namelen);

#if MBEDTLS

	int ret = 0/*, i*/;
    //const char *pers = "ssl_client_tls_wrapper";
    const char *pers = "ssl_client2";
	#if defined(__HARMONY_OS__)
	TLSDataParams *tlsDataParams = (TLSDataParams *)malloc(sizeof(TLSDataParams));
    #else
	TLSDataParams *tlsDataParams = (TLSDataParams *)malloc(ALIGN(sizeof(TLSDataParams)));
    #endif
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)	/*<---------------------- Dean: this is defined by default */
	unsigned char psk[MBEDTLS_PSK_MAX_LEN];
	size_t psk_len = MBEDTLS_PSK_MAX_LEN;
#endif

	char portBuffer[6];
	TLSConnectParams tlsConnectParams;


	*pTlsHandle = (long)tlsDataParams;
	//DBG_PRINT("  . HTTPWrapperSSLConnect2 start	...\n");
	//DBG_PRINT("  . HTTPWrapperSSLConnect2 start(0x%X => 0x%X)	...\n", *pTlsHandle, (int)tlsDataParams);

    memset(tlsDataParams,0x00,sizeof(TLSDataParams));

	tlsConnectParams.pDestinationURL = hostname;
	tlsConnectParams.DestinationPort = name->sin_port;
	tlsConnectParams.ServerVerificationFlag = false;
	tlsConnectParams.timeout_ms = 10*1000;

   /*
     * Make sure memory references are valid.
     */
	mbedtls_net_init(&(tlsDataParams->server_fd));
	mbedtls_ssl_init(&(tlsDataParams->ssl));
	mbedtls_ssl_config_init(&(tlsDataParams->conf));
	mbedtls_ctr_drbg_init(&(tlsDataParams->ctr_drbg));
#if defined(MBEDTLS_X509_CRT_PARSE_C)
	mbedtls_x509_crt_init(&(tlsDataParams->cacert));
	mbedtls_x509_crt_init(&(tlsDataParams->clicert));
	mbedtls_pk_init(&(tlsDataParams->pkey));
#endif
#if defined(MBEDTLS_THREADING_C)
	mbedtls_mutex_init( &mbedtls_threading_gmtime_mutex );
#endif
#if 0//defined(MBEDTLS_DEBUG_C)
	mbedtls_debug_set_threshold( 0 );
#endif

    /*
     * 0. Initialize the RNG and the session data
     */
	DBG_PRINT("\n  . Seeding the random number generator...");

	/*
	  * 1. set which one
	  * 2. refer to ssl is much easier
	  */
	mbedtls_entropy_init(&(tlsDataParams->entropy));
	if((ret = mbedtls_ctr_drbg_seed(&(tlsDataParams->ctr_drbg), mbedtls_entropy_func, &(tlsDataParams->entropy),
									(const unsigned char *) pers, strlen(pers))) != 0) {
		DBG_PRINT(" failed\n  ! mbedtls_ctr_drbg_seed returned -0x%x\n", -ret);
		return ret;
	}
    DBG_PRINT( " ok\n" );

    /*
     * 2. Start the connection
     */
	snprintf(portBuffer, 6, "%d", tlsConnectParams.DestinationPort);
	DBG_PRINT("  . Connecting to %s/%s...", tlsConnectParams.pDestinationURL, portBuffer);
	if((ret = mbedtls_net_connect(&(tlsDataParams->server_fd), tlsConnectParams.pDestinationURL,
								  portBuffer, MBEDTLS_NET_PROTO_TCP)) != 0) {
		DBG_PRINT(" failed\n  ! mbedtls_net_connect returned -0x%x\n\n", -ret);
		return ret;
		/*switch(ret) {
			case MBEDTLS_ERR_NET_SOCKET_FAILED:
				return NETWORK_ERR_NET_SOCKET_FAILED;
			case MBEDTLS_ERR_NET_UNKNOWN_HOST:
				return NETWORK_ERR_NET_UNKNOWN_HOST;
			case MBEDTLS_ERR_NET_CONNECT_FAILED:
			default:
				return NETWORK_ERR_NET_CONNECT_FAILED;
		};*/
	}

#if !defined(_HARMONY_OS_PORTING_)
	ret = mbedtls_net_set_block(&(tlsDataParams->server_fd));
	if(ret != 0) {
		DBG_PRINT(" failed\n  ! net_set_(non)block() returned -0x%x\n\n", -ret);
		return ret;
	}
#endif

	DBG_PRINT(" ok\n");

    /*
     * 3. Setup stuff
     */
	DBG_PRINT("  . Setting up the SSL/TLS structure...");
	if((ret = mbedtls_ssl_config_defaults(&(tlsDataParams->conf), MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM,
										  MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
		DBG_PRINT(" failed\n  ! mbedtls_ssl_config_defaults returned -0x%x\n\n", -ret);
		return ret;
		//return SSL_CONNECTION_ERROR;
	}

#if 0 //defined(MBEDTLS_X509_CRT_PARSE_C)
	mbedtls_ssl_conf_verify(&(tlsDataParams->conf), _tls_verify_cert, NULL);
#endif

	if(tlsConnectParams.ServerVerificationFlag == true) {
		mbedtls_ssl_conf_authmode(&(tlsDataParams->conf), MBEDTLS_SSL_VERIFY_REQUIRED);
	} else {
		mbedtls_ssl_conf_authmode(&(tlsDataParams->conf), MBEDTLS_SSL_VERIFY_OPTIONAL);
	}
	mbedtls_ssl_conf_rng(&(tlsDataParams->conf), mbedtls_ctr_drbg_random, &(tlsDataParams->ctr_drbg));
	//#if !defined(__HARMONY_OS__)
    mbedtls_ssl_conf_dbg( &(tlsDataParams->conf), my_debug, stdout );
	//#endif

	mbedtls_ssl_conf_read_timeout(&(tlsDataParams->conf), tlsConnectParams.timeout_ms);

#if defined(MBEDTLS_X509_CRT_PARSE_C)
	mbedtls_ssl_conf_ca_chain(&(tlsDataParams->conf), &(tlsDataParams->cacert), NULL);
	if((ret = mbedtls_ssl_conf_own_cert(&(tlsDataParams->conf), &(tlsDataParams->clicert), &(tlsDataParams->pkey))) !=
	   0) {
		DBG_PRINT(" failed\n  ! mbedtls_ssl_conf_own_cert returned -0x%X(%d)\n\n", -ret, ret);
		return ret;
	}
#endif
#if defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED)
	if( ( ret = mbedtls_ssl_conf_psk( &(tlsDataParams->conf), psk, psk_len,
							 (const unsigned char *) "Client_identity",
							 strlen( "Client_identity" ) ) ) != 0 )
	{
		DBG_PRINT( " failed\n	! mbedtls_ssl_conf_psk returned -0x%X(%d)\n\n", -ret, ret);
		return ret;
	}
#endif

	/* Use the AWS IoT ALPN extension for MQTT if port 443 is requested. */
	//if(443 == tlsConnectParams.DestinationPort) {
	//	if((ret = mbedtls_ssl_conf_alpn_protocols(&(tlsDataParams->conf), alpnProtocols)) != 0) {
	//		DBG_PRINT(" failed\n  ! mbedtls_ssl_conf_alpn_protocols returned -0x%x\n\n", -ret);
	//		return ret;
	//	}
	//}

	/* Assign the resulting configuration to the SSL context. */
	if((ret = mbedtls_ssl_setup(&(tlsDataParams->ssl), &(tlsDataParams->conf))) != 0) {
		DBG_PRINT(" failed\n  ! mbedtls_ssl_setup returned -0x%x\n\n", -ret);
		return ret;
	}
#if defined(MBEDTLS_X509_CRT_PARSE_C)
	if((ret = mbedtls_ssl_set_hostname(&(tlsDataParams->ssl), tlsConnectParams.pDestinationURL)) != 0) {
		DBG_PRINT(" failed\n  ! mbedtls_ssl_set_hostname returned -0x%X\n\n", -ret);
		return ret;
	}
#endif
	DBG_PRINT("\n\nSSL state connect : %d ", tlsDataParams->ssl.state);
//#ifdef	_HARMONY_OS_PORTING_
//	mbedtls_ssl_set_bio(&(tlsDataParams->ssl), &(tlsDataParams->server_fd), mbedtls_net_send, mbedtls_net_recv,
//						my_mbedtls_net_recv_timeout);
//#else
	mbedtls_ssl_set_bio(&(tlsDataParams->ssl), &(tlsDataParams->server_fd), mbedtls_net_send, mbedtls_net_recv,
						mbedtls_net_recv_timeout);
//#endif

	DBG_PRINT(" ok\n");

    /*
     * 4. Handshake
     */
	DBG_PRINT("  . Performing the SSL/TLS handshake...");

    //fflush( stdout );
	while((ret = mbedtls_ssl_handshake(&(tlsDataParams->ssl))) != 0) {
		if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
			DBG_PRINT(" failed\n  ! mbedtls_ssl_handshake returned -0x%x\n", -ret);
			if(ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
				DBG_PRINT("    Unable to verify the server's certificate. "
							  "Either it is invalid,\n"
							  "    or you didn't set ca_file or ca_path "
							  "to an appropriate value.\n"
							  "    Alternatively, you may want to use "
							  "auth_mode=optional for testing purposes.\n");
			}
			return ret;
		}
	}

	DBG_PRINT(" ok\n	[ Protocol is %s ]\n	[ Ciphersuite is %s ]\n",
		mbedtls_ssl_get_version(&(tlsDataParams->ssl)), mbedtls_ssl_get_ciphersuite(&(tlsDataParams->ssl)));

	if((ret = mbedtls_ssl_get_record_expansion(&(tlsDataParams->ssl))) >= 0) {
		DBG_PRINT("    [ Record expansion is %d ]\n", ret);
	} else {
		DBG_PRINT("    [ Record expansion is unknown (compression) ]\n");
	}

	DBG_PRINT("  . Verifying peer X.509 certificate...");

	if(tlsConnectParams.ServerVerificationFlag == true) {
		char vrfy_buf[512];
		if((tlsDataParams->flags = mbedtls_ssl_get_verify_result(&(tlsDataParams->ssl))) != 0) {
			DBG_PRINT(" failed\n");
			mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "	! ", tlsDataParams->flags);
			DBG_PRINT("%s\n", vrfy_buf);
			//ret = SSL_CONNECTION_ERROR;
			ret = -4;
		} else {
			DBG_PRINT(" ok\n");
			ret = HTTPCLIENT_SUCCESS;
		}
	} else {
		DBG_PRINT(" Server Verification skipped\n");
		ret = HTTPCLIENT_SUCCESS;
	}

#ifdef ENABLE_IOT_DEBUG
	unsigned char buf[MBEDTLS_DEBUG_BUFFER_SIZE];
	if (mbedtls_ssl_get_peer_cert(&(tlsDataParams->ssl)) != NULL) {
		DBG_PRINT("  . Peer certificate information    ...\n");
		mbedtls_x509_crt_info((char *) buf, sizeof(buf) - 1, "		", mbedtls_ssl_get_peer_cert(&(tlsDataParams->ssl)));
		DBG_PRINT("%s\n", buf);
	}
#endif

	mbedtls_ssl_conf_read_timeout(&(tlsDataParams->conf), 10);

	DBG_PRINT("  . HTTPWrapperSSLConnect2 end	...\n");
	return HTTPCLIENT_SUCCESS;

#else
	UNUSED(pTlsHandle);
	UNUSED(namelen);
	UNUSED(hostname);
	return -1;
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperSSLNegotiate(long s,const struct sockaddr *name,int namelen,char *hostname)
{
	UNUSED(s);
	UNUSED(name);
	UNUSED(namelen);
	UNUSED(hostname);
#if MBEDTLS
	return 0;
#else
	return -1;
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperSSLSend(long pTlsHandle, unsigned char *buf, int len,int flags)
{
	UNUSED(flags);

#if MBEDTLS
	int written_so_far;
	BOOL isErrorFlag = false;
	int frags;
	int ret = 0;
	TLSDataParams *tlsDataParams = (TLSDataParams*)pTlsHandle;

	//DBG_PRINT("  . HTTPWrapperSSLSend start(0x%X)	...\n", pTlsHandle);

	for(written_so_far = 0, frags = 0;
		written_so_far < len; written_so_far += ret, frags++) {
		while((ret = mbedtls_ssl_write(&(tlsDataParams->ssl), buf + written_so_far, len - written_so_far)) <= 0) {
			if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
				DBG_PRINT(" failed\n  ! mbedtls_ssl_write returned -0x%x\n\n", -ret);
				/* All other negative return values indicate connection needs to be reset.
				* Will be caught in ping request so ignored here */
				isErrorFlag = true;
				break;
			}
		}
		if(isErrorFlag) {
			break;
		}
	}

	//DBG_PRINT("  . HTTPWrapperSSLSend end	(total:%d, send:%d, error:%d)...\n", len, written_so_far, isErrorFlag);

	if(isErrorFlag) {
		return -1;
	} else if(written_so_far != len) {
		return -1;
	}

	return written_so_far;
#else
	UNUSED(pTlsHandle);
	UNUSED(buf);
	UNUSED(len);
	return -1;
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

int HTTPWrapperSSLRecv(long pTlsHandle, unsigned char *buf, int len,int flags)
{
	UNUSED(flags);

#if MBEDTLS
	TLSDataParams *tlsDataParams = (TLSDataParams*)pTlsHandle;
	mbedtls_ssl_context *ssl = &(tlsDataParams->ssl);
	size_t rxLen = 0;
	int ret = 0;

	//DBG_PRINT("  . HTTPWrapperSSLRecv start	...\n");
	while (len > 0) {
		// This read will timeout after IOT_SSL_READ_TIMEOUT if there's no data to be read
		ret = mbedtls_ssl_read(ssl, buf, len);
		if (ret > 0) {
			rxLen += ret;
			buf += ret;
			len -= ret;
		} else if (ret == 0 || (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != MBEDTLS_ERR_SSL_TIMEOUT)) {
			break;
		}
	}
	//DBG_PRINT("  . HTTPWrapperSSLRecv end	...\n");

	if (len == 0 || ret == 0) {
		return rxLen;
	}
	return ret;
#else
	UNUSED(pTlsHandle);
	UNUSED(buf);
	UNUSED(len);
	return -1;
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int HTTPWrapperSSLRecvPending(long s)
{
	UNUSED(s);

#if MBEDTLS
	return 1;
#else
    return -1;
#endif
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
int HTTPWrapperSSLClose(long pTlsHandle)
{
#if MBEDTLS
	TLSDataParams *tlsDataParams = (TLSDataParams*)pTlsHandle;
	mbedtls_ssl_context *ssl = &(tlsDataParams->ssl);
	int ret = 0;

	//DBG_PRINT("  . HTTPWrapperSSLClose start	...\n");
	do {
		ret = mbedtls_ssl_close_notify(ssl);
	} while(ret == MBEDTLS_ERR_SSL_WANT_WRITE);

	/* All other negative return values indicate connection needs to be reset.
	 * No further action required since this is disconnect call */

	mbedtls_net_free(&(tlsDataParams->server_fd));

	mbedtls_x509_crt_free(&(tlsDataParams->clicert));
	mbedtls_x509_crt_free(&(tlsDataParams->cacert));
	mbedtls_pk_free(&(tlsDataParams->pkey));
	mbedtls_ssl_free(&(tlsDataParams->ssl));
	mbedtls_ssl_config_free(&(tlsDataParams->conf));
	mbedtls_ctr_drbg_free(&(tlsDataParams->ctr_drbg));
	mbedtls_entropy_free(&(tlsDataParams->entropy));
	if (tlsDataParams) {
		free(tlsDataParams);
	}
	//DBG_PRINT("  . HTTPWrapperSSLClose end	...\n");

	return HTTPCLIENT_SUCCESS;
#else
	UNUSED(pTlsHandle);
	return -1;
#endif

}
